package com.hilinos.common.util;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;


/**
 * 实体对象工具类，采用jackson实现<br>
 * 经过测试，ObjectMapper单例模式下并加入同步锁，比普通new性能高30+倍且极大程度减少了内存回收，初步判断ObjectMapper会在初始化时载入配置，因此本次采用单例模式
 *
 * @author qhl
 */
@Slf4j
public class EntityUtils {
    /**
     * Mapper同步锁，保障ObjectMapper只有一个线程处理
     */
    private static final Object MAPPER_LOCK = new Object();
    /**
     * Jackson转换Mapper
     */
    private static final ObjectMapper MAPPER = new ObjectMapper();
    /**
     * 空的json字符串
     */
    private static final String EMPTY_JSON = "{}";

    static {
        MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    /**
     * 转换json字符串
     *
     * @param entity 被转换的实体
     * @return json字符串，如果转换失败，则返回null
     */
    public static String toJsonString(@NonNull Object entity) {
        try {
            synchronized (MAPPER_LOCK) {
                return MAPPER.writeValueAsString(entity);
            }
        } catch (Exception e) {
            log.error("[数据转换]{} 转换json字符串失败", entity.getClass().getName());
        }
        return EMPTY_JSON;
    }


    /**
     * 实体类转实体类
     *
     * @param source 被转换的实体
     * @param cls    转换的类
     * @return 对应实体类，如果转换失败，则返回null
     */
    public static <T> T entity2Entity(@NonNull Object source, Class<T> cls) {
        if (log.isDebugEnabled()) {
            log.debug("[数据转换]entity2Entity转换开始:{}", source);
        }
        if (source instanceof String) {
            return jsonStr2Entity((String) source, cls);
        }
        try {
            String jsonStr = toJsonString(source);
            if (jsonStr == null) {
                return null;
            }
            synchronized (MAPPER_LOCK) {
                return MAPPER.readValue(jsonStr, cls);
            }
        } catch (Exception e) {
            log.error("[数据转换]\n对象转换失败\nsource : {}  \ndestination : {}", source.getClass().getName(), cls.getName());
        }
        return null;
    }

    /**
     * 实体类转实体类
     *
     * @param source 被转换的实体
     * @param cls    转换的类
     * @return 对应实体类，如果转换失败，则返回null
     */
    public static <T> T entity2Entity(@NonNull Object source, TypeReference<T> cls) {
        if (log.isDebugEnabled()) {
            log.debug("[数据转换]entity2Entity转换开始:{}", source);
        }
        if (source instanceof String) {
            return jsonStr2Entity((String) source, cls);
        }
        try {
            String jsonStr = toJsonString(source);
            if (jsonStr == null) {
                return null;
            }
            synchronized (MAPPER_LOCK) {
                return MAPPER.readValue(jsonStr, cls);
            }
        } catch (Exception e) {
            log.warn("[数据转换]\n对象转换失败\nsource : {}  \ndestination : {}", source.getClass().getName(), cls.getClass().getName());
        }
        return null;
    }

    /**
     * 批量转换，实体类转实体类
     *
     * @param sources 实体类集合
     * @param <T>     被转换的对象类
     * @return 返回转换结果，用list存放
     * @throws Exception 当源实体类集合为空时或者其中一个转换失败时，会抛出异常
     */
    public static <T> List<T> entities2Entities(Collection<?> sources, Class<T> cls) throws Exception {
        if (sources == null) {
            throw new Exception("被转换对象为空");
        }
        List<T> resultList = new ArrayList<>();
        for (Object source : sources) {
            T result = entity2Entity(source, cls);
            if (result == null) {
                throw new Exception("转换失败");
            }
            resultList.add(result);
        }
        return resultList;
    }

    /**
     * json字符串转实体类
     *
     * @param jsonStr json字符串
     * @return 对应实体类，如果转换失败，则返回null
     */
    public static <T> T jsonStr2Entity(String jsonStr, Class<T> cls) {
        if (ObjectUtils.isEmpty(jsonStr)) {
            return null;
        }
        try {
            synchronized (MAPPER_LOCK) {
                return MAPPER.readValue(jsonStr, cls);
            }
        } catch (Exception e) {
            log.warn("\n[数据转换]\n" +
                    "调用方法\t:\t{}\n" +
                    "转换结果\t:\t失败\n" +
                    "数据源\t:\t{}\n" +
                    "目标类型\t:\t{}", Thread.currentThread().getStackTrace()[1].getMethodName(), jsonStr, cls.getName());
        }
        return null;
    }

    /**
     * json字符串转实体类
     *
     * @param jsonStr json字符串
     * @return 对应实体类，如果转换失败，则返回null
     */
    public static <T> T jsonStr2Entity(String jsonStr, TypeReference<T> cls) {
        if (ObjectUtils.isEmpty(jsonStr)) {
            return null;
        }
        try {
            synchronized (MAPPER_LOCK) {
                return MAPPER.readValue(jsonStr, cls);
            }
        } catch (Exception e) {
            log.warn("\n[数据转换]\n" +
                            "调用方法\t:\t{}\n" +
                            "转换结果\t:\t失败 {}\n" +
                            "数据源\t:\t{}\n" +
                            "目标类型\t:\t{}",
                    Thread.currentThread().getStackTrace()[1].getMethodName(), e.getMessage(), jsonStr, cls.getClass().getName());
        }
        return null;
    }
}
